#include "RHIFramebuffer.h"
#include "../RenderState.h"
#include "../../Game/Process/RenderProcess.h"


RHIFrameBuffer::BuilderDetails::BuilderDetails()
{
	count = GRenderState->GetFrameCount();

	width = 0;

	height = 0;

	layers = 1;
}

RHIFrameBuffer::Builder::Builder() noexcept = default;
RHIFrameBuffer::Builder::~Builder() noexcept = default;
RHIFrameBuffer::Builder::Builder(RHIFrameBuffer::Builder const& rhs) noexcept = default;
RHIFrameBuffer::Builder::Builder(RHIFrameBuffer::Builder && rhs) noexcept = default;
RHIFrameBuffer::Builder& RHIFrameBuffer::Builder::operator=(RHIFrameBuffer::Builder const& rhs) noexcept = default;
RHIFrameBuffer::Builder& RHIFrameBuffer::Builder::operator=(RHIFrameBuffer::Builder && rhs) noexcept = default;

RHIFrameBuffer::Builder& RHIFrameBuffer::Builder::AddRenderTarget(RefCountPtr<RenderTarget> renderTarget) noexcept
{
	mImpl->renderTargets.push_back(renderTarget);
	return *this;
}

RHIFrameBuffer::Builder& RHIFrameBuffer::Builder::SetRenderPass(RefCountPtr<RHIRenderPass> renderPass) noexcept
{
	mImpl->renderPass = renderPass;
	return *this;
}

RHIFrameBuffer::Builder& RHIFrameBuffer::Builder::SetCount(uint32 count) noexcept
{
	mImpl->count = count;
	return *this;
}

RHIFrameBuffer::Builder& RHIFrameBuffer::Builder::SetWidth(uint32 width) noexcept
{
	mImpl->width = width;
	return *this;
}

RHIFrameBuffer::Builder& RHIFrameBuffer::Builder::SetHeight(uint32 height) noexcept
{
	mImpl->height = height;
	return *this;
}

RHIFrameBuffer::Builder& RHIFrameBuffer::Builder::SetLayers(uint32 layers) noexcept
{
	mImpl->layers = layers;
	return *this;
}

RefCountPtr<RHIFrameBuffer> RHIFrameBuffer::Builder::Build()
{
	assert(IsInRenderThread());
	return RefCountPtr<RHIFrameBuffer>(NewObject<RHIFrameBuffer>(*mImpl));
}

RefCountPtr<RHIFrameBuffer> RHIFrameBuffer::Builder::Build(RenderProcess * process, const std::string & name)
{
	assert(IsInRenderThread());
	RHIFrameBuffer* frameBuffer = NewObject<RHIFrameBuffer>(*mImpl);
	process->AddFrameBuffer(name, frameBuffer);
	return RefCountPtr<RHIFrameBuffer>(frameBuffer);
}

void RHIFrameBuffer::ReleaseResource()
{
	if (IsInRenderThread())
	{
		if (mProcess != nullptr)
		{
			mProcess->RemoveFrameBuffer(mName, this);
		}
		DeleteObject<RHIFrameBuffer>(this);
	}
	else
	{
		AsyncTask(GRenderThreadId, [this]() {
			if (mProcess != nullptr)
			{
				mProcess->RemoveFrameBuffer(mName, this);
			}
			DeleteObject<RHIFrameBuffer>(this);
			});
	}
}

RHIFrameBuffer::~RHIFrameBuffer()
{
	for (uint32 i = 0; i < mFrameBuffers.size(); ++i)
	{
		GRenderState->DelayGCFrameBuffer(mFrameBuffers[i]);
	}
}

VkFramebuffer RHIFrameBuffer::GetFrameBuffer(uint32 index /*= UINT32_MAX*/) const
{
	if (index == UINT32_MAX)
		return mFrameBuffers[GRenderState->GetFrameIndex()];
	assert(index < mFrameBuffers.size());
	return mFrameBuffers[index];
}

VkRenderPassBeginInfo RHIFrameBuffer::GetRenderPassBeginInfo(uint32 index)
{
	mRenderPassBeginInfo.framebuffer = GetFrameBuffer(index);
	return mRenderPassBeginInfo;
}

RHIFrameBuffer::RHIFrameBuffer(BuilderDetails & builderDetails)
{
	assert(builderDetails.renderPass.IsValid());

	mLayers = builderDetails.layers;

	if (builderDetails.renderTargets.size() == 0)
	{
		mRenderTargetViewMap = builderDetails.renderPass->GetRenderTargetViewMap();
		mClearValues = builderDetails.renderPass->GetClearValues();

		if (builderDetails.width != 0)
		{
			// If set the width and height by youself, the render target size of renderPass must need to be zero
			// If the render target size of renderPass not zero, we directly get the width and height from render target
			mWidth = builderDetails.width;
			assert(builderDetails.renderPass->GetRenderTargetSize() == 0);
		}
		else
			mWidth = builderDetails.renderPass->GetWidth();

		if (builderDetails.height != 0)
		{
			mHeight = builderDetails.height;
			assert(builderDetails.renderPass->GetRenderTargetSize() == 0);
		}
		else
			mHeight = builderDetails.renderPass->GetHeight();

		mFrameBuffers.resize(builderDetails.count, VK_NULL_HANDLE);
		for (uint32 i = 0; i < builderDetails.count; ++i)
		{
			std::vector<VkImageView> attachments = mRenderTargetViewMap[i];

			VkFramebufferCreateInfo frameBufferCreateInfo{};
			frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
			frameBufferCreateInfo.renderPass = builderDetails.renderPass->GetRenderPass();
			frameBufferCreateInfo.attachmentCount = static_cast<uint32>(attachments.size());
			frameBufferCreateInfo.pAttachments = attachments.data();
			frameBufferCreateInfo.width = mWidth;
			frameBufferCreateInfo.height = mHeight;
			frameBufferCreateInfo.layers = mLayers;

			VK_CHECK_RESULT(vkCreateFramebuffer(GVulkanRHI->GetLogicalDevice(), &frameBufferCreateInfo, nullptr, &mFrameBuffers[i]));
		}
	}
	else
	{
		// The size of render target need to be same
		assert(builderDetails.renderTargets.size() == builderDetails.renderPass->GetRenderTargetSize());

		// If the render target exist, do not allow get the weith and height by youself
		assert(builderDetails.width == 0 && builderDetails.height == 0);

		mClearValues.resize(builderDetails.renderTargets.size(), {});

		// Get the image view from render target and check the width height
		mRenderTargetViewMap.resize(builderDetails.count, {});
		for (uint32 i = 0; i < builderDetails.renderTargets.size(); ++i)
		{
			if (i == 0)
			{
				mWidth = builderDetails.renderTargets[i]->GetWitdh();
				mHeight = builderDetails.renderTargets[i]->GetHeight();
			}
			else
			{
				assert(mWidth == builderDetails.renderTargets[i]->GetWitdh());
				assert(mHeight == builderDetails.renderTargets[i]->GetHeight());
			}
			mClearValues[i] = builderDetails.renderTargets[i]->GetClearValue();
			// Get renderTargetView
			for (uint32 j = 0; j < builderDetails.count; ++j)
			{
				mRenderTargetViewMap[j].push_back(builderDetails.renderTargets[i]->GetImageView(j));
			}
		}

		mFrameBuffers.resize(builderDetails.count, VK_NULL_HANDLE);
		for (uint32 i = 0; i < builderDetails.count; ++i)
		{
			std::vector<VkImageView> attachments = mRenderTargetViewMap[i];

			VkFramebufferCreateInfo frameBufferCreateInfo{};
			frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
			frameBufferCreateInfo.renderPass = builderDetails.renderPass->GetRenderPass();
			frameBufferCreateInfo.attachmentCount = static_cast<uint32>(attachments.size());
			frameBufferCreateInfo.pAttachments = attachments.data();
			frameBufferCreateInfo.width = mWidth;
			frameBufferCreateInfo.height = mHeight;
			frameBufferCreateInfo.layers = mLayers;

			VK_CHECK_RESULT(vkCreateFramebuffer(GVulkanRHI->GetLogicalDevice(), &frameBufferCreateInfo, nullptr, &mFrameBuffers[i]));
		}

	}

	mRenderPassBeginInfo = {};
	mRenderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
	mRenderPassBeginInfo.renderPass = builderDetails.renderPass->GetRenderPass();

	mRenderPassBeginInfo.renderArea.offset = { 0, 0 };
	mRenderPassBeginInfo.renderArea.extent = { mWidth, mHeight };

	mRenderPassBeginInfo.clearValueCount = static_cast<uint32>(mClearValues.size());
	mRenderPassBeginInfo.pClearValues = mClearValues.data();
}